﻿using System;
using Services;
using Utilities;
using System.Linq;
using JigLibX.Physics;
using System.Threading;
using JigLibX.Geometry;
using JigLibX.Collision;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.GamerServices;
using SpaceBattle.Menu;

namespace SpaceBattle.Logic.SampleLevel
{
  using Nuke = Projectile;
  using NukeData = ProjectileData;

  public class CampaignMode : GamePlayLayer
  {
    public static int NUMBER_OF_LEVELS = 15;

    StateEntry current_state;
    LevelInfo level_info;

    public CampaignMode(Game game, BaseLevelStats current_score, LevelInfo level_info, ShipType player_ship, int num_lives)
      : base(game, current_score, player_ship, num_lives)
    {
      current_state = Leaderboard.GetCampaignState();
      this.level_info = level_info;
    }

    public override void Initialize()
    {
      base.Initialize();
    }

    protected override void LoadContent()
    {
      base.LoadContent();
    }

    protected override void start_level()
    {
      /// Inizializziamo la fisica.
      physics = new PhysicsSystem()
      {
        CollisionSystem = new CollisionSystemSAP() { UseSweepTests = true },
        Gravity = Vector3.Zero,

        NumCollisionIterations = 2, //8
        NumContactIterations = 2, //8
        NumPenetrationRelaxtionTimesteps = 5, //15
      };

      StructAllocatorManager.Reset();

      var c = ConfigurationService.GetDefaulConfiguration();
      ConfigurationService.ChangeConfiguration(Game, c);

      var num_enemies = level_info.enemy_waves[0].ships.Count;
      var sh = c.Ships[(int)player_ship_type].MaxShield;

      CurrentState = new State()
      {
        asteroids = new ArrayList<Asteroid>(35),
        enemies = new ArrayList<Ship>(15),
        bosses = new ArrayList<Ship>(5),
        primary_weapons = new ArrayList<Projectile>(100),
        secondary_weapons = new ArrayList<Nuke>(25),
        shockwaves = new ArrayList<ShockWave>(5),
        bonuses = new ArrayList<Bonus>(5),
        player = new PlayerShip()
        {
          AsShip = CreateShip(Vector3.Zero, scene_service[player_ship_type].BoundingSphere, 20000.0f,
            player_ship_type, Faction.Player, (int)MathHelper.Clamp(num_enemies / 2, 2, 5)),
          warp_type = WarpType.None,
          num_lives = current_score.NumLivesRemaining,
          shield = sh,
          energy = 1.0f,
          warp_state = 1.0f,
          waypoint = new MaybeWaypoint() { HasValue = false, Position = Vector3.Zero },
          age = 0.0f,
          INVULNERABILITY_SPAWN_TIME = 3.0f,
          shields_spawn_time = new float[] { 0, 0, 0, 0 }
        }
      };

      var level_enemies = new ArrayList<Ship>(num_enemies);
      for (int i = 0; i < num_enemies; i++)
      {
        var a = MathHelper.Pi * 2.0f * i / num_enemies;
        var R = Matrix.CreateRotationY(a);

        ShipType enemy_type = level_info.enemy_waves[0].ships[i];
        ModelWithVolumes modello;
        modello = scene_service[enemy_type];

        level_enemies.Add(CreateShip(
            asteroidsFreeSpace(Vector3.Transform(Vector3.Right * 8.0f, R) + CurrentState.player.geometry.Position, Vector3.Transform(Vector3.Right, R), modello.BoundingSphere),
            modello.BoundingSphere, 5000.0f, enemy_type,
            Faction.Enemy, (int)enemy_type + 2));
      }

      CurrentState.enemies = level_enemies;
      scene_bs = SceneBoundingSphere;

      wave_left = 0;
      spawn_boss = false;

      NewExplosions = new ArrayList<Explosion>(25);
      NewShockWaves = new ArrayList<ShockWave>(5);
      effects_manager = Game.Services.GetService(typeof(EffectsManager)) as EffectsManager;
    }


    public override void Update(GameTime gameTime)
    {
      var dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
      for (int i = 0; i < current_targets.Count; i++)
      {
        var target = current_targets[i];
        var wi = target.weapon_index;
        var w = player_ship.PrimaryWeapons[wi];
        if (target.StillValid == false)
        {
          current_targets.RemoveAt(i);
          i--;
          continue;
        }

        target.shooting_timer += dt;
        if (target.keep_shooting && target.shooting_timer > w.FireRate)
        {
          var user = CurrentState.player.AsShip;
          var prev_active_weapon = CurrentState.player.primary_active;
          CurrentState.player.primary_active = wi;
          ShootWeapon(w.Type, user,
            target.target_ship, Vector3.Zero, Vector3.Zero,
            target.target_nuke, target.target_bonus,
            target.target_type);
          target.shooting_timer = 0.0f;
          CurrentState.player.primary_active = prev_active_weapon;
        }
        current_targets[i] = target;
      }

      if (CurrentState.enemies.Count <= 0 && CurrentState.bosses.Count <= 0 && enemy_to_add <= 0)
        if (wave_left + 1 >= level_info.enemy_waves.Count)
        {
            if (enemy_to_add <= 0)
            {
                var cs = CurrentScore;
                cs.Credits += level_info.given_credits;
                CurrentScore = cs;
                LevelWon();
            }
        }
        else
        {
          wave_left++;

          enemy_to_add = level_info.enemy_waves[(int)wave_left].ships.Count;
          enemy_spawn_timer = 0.0f;
        }

      if (enemy_to_add > 0)
      {
        enemy_spawn_timer += dt;
        if (enemy_spawn_timer >= enemy_spawn_delay)
        {
          var i = level_info.enemy_waves[(int)wave_left].ships.Count;
          var ship_t = level_info.enemy_waves[(int)wave_left].ships[i - enemy_to_add];
          Ship ship_to_add = (int)(ship_t) >= (int)ShipType.Boss1 ? generate_new_boss(ship_t) : generate_new_enemy(ship_t);
          if ((int)ship_t >= (int)ShipType.Boss1)
          {
            CurrentState.bosses.Add(ship_to_add);
            enemy_to_add = 0;
          }
          else
            CurrentState.enemies.Add(ship_to_add);
          warps.Add(new EffectsManager.EnemyWarp(effects_manager, ship_to_add));
          enemy_to_add--;
          enemy_spawn_timer = 0;
        }
      }

      for (int j = 0; j < warps.Count; j++)
      {
        warps[j].Update(dt);

        if (warps[j].warping_out_timer < 0)
        {
          warps[j].particle.IsActive = false;
          warps.Remove(warps[j]);
          j--;
        }
      }

      if (CurrentState.player.stats.CurrentLife <= 0.0f || (CurrentState.enemies.Count <= 0 && CurrentState.bosses.Count <= 0 && enemy_to_add <= 0))
      {
        this.Enabled = false;
      }

      // Aggiorniamo il timer di slow-motion
      CurrentState.slow_motion_timer = Math.Max(0.0f, CurrentState.slow_motion_timer -
      (float)gameTime.ElapsedGameTime.TotalSeconds);

      base.Update(gameTime);
    }

    protected override void OnShipDestroyed(Ship ship)
    {
      for (int i = 0; i < current_targets.Count; i++)
      {
        if (current_targets[i].target_type == TargetType.Ship && current_targets[i].target_ship.self.Equals(ship.self))
        {
          current_targets.RemoveAt(i);
          break;
        }
      }

      current_score.EnemyShipsDestroyed++;
      //scene_service.OnScoringEvent(ship.geometry.Position, BaseLevelStats.ENEMY_DESTROYED);

      var w = level_info.enemy_waves[(int)wave_left];
      var a = (float)w.ships.Count / (float)w.bonuses.Count;

      if (w.bonuses.Count > 0)
        if (random.NextDouble() <= a)
        {
          AddBonus(ship.geometry.Position, w.bonuses[0]);
          w.bonuses.RemoveAt(0);
        }
    }
  }
}
